﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using HtmlAgilityPack;
using GeneralTools;
using System.Net;
using System.IO;
using System.Threading;

namespace MtgLib
{
    [Serializable]
    public enum EDownloadPicturesFrom : int
    {
        Unknown = 0,
        MagicCards_info = 1, MtgVault_com = 2
    };

    public static class MtgCardDownloader
    {
        private static bool _allowCaching = true;
        public static bool AllowDiskCaching
        {
            get
            {
                return _allowCaching;
            }
            set
            {
                _allowCaching = value;
            }
        }

        private static string _dickCachePath;
        public static string DiskCachePath
        {
            get;
            set;
        }

        public static long DiskCacheMaxSize
        {
            get;
            set;
        }

        private static EDownloadPicturesFrom _downloadFrom;
        public static EDownloadPicturesFrom DownloadPicturesFrom
        {
            get { return _downloadFrom; }
            set { _downloadFrom = value; }
        }

        public static TimeSpan _diskCacheRemoveAfter = new TimeSpan(14, 0, 0, 0);
        public static TimeSpan DiskCacheRemoveAfter
        {
            get { return _diskCacheRemoveAfter; }
            set { _diskCacheRemoveAfter = value; }
        }


        public static Image GetCardImage(string cardName)
        {
            Image retImage = null;
            if (AllowDiskCaching)
            {
                
                retImage = getImageFromCache(cardName);
                if (retImage != null) return retImage;
            }

            retImage = downloadCardPicture(cardName);
            if (AllowDiskCaching && retImage != null) saveImageToCache(cardName, retImage);
            return retImage;
        }

        private static void CheckCache()
        {
            if (string.IsNullOrEmpty(DiskCachePath) || !Directory.Exists(DiskCachePath)) return;
            if (!string.IsNullOrEmpty(DiskCachePath) && Directory.Exists(DiskCachePath))
            {

            }

            var files = Directory.GetFiles(DiskCachePath);
            List<string> lfles = files.ToList();

            lfles.Sort(delegate(string n1, string n2)
            {
                FileInfo fi1 = new FileInfo(n1);
                FileInfo fi2 = new FileInfo(n2);

                return fi1.CreationTime.CompareTo(fi2.CreationTime);
            });

            long totalFilesSize = 0;

            foreach (string str in lfles)
            {
                FileInfo fi = new FileInfo(str);
                totalFilesSize += fi.Length;
            }

            foreach (string str in lfles)
            {
                FileInfo fi = new FileInfo(str);
                long fileLen = fi.Length;
                if (DiskCacheMaxSize != 0 && totalFilesSize > DiskCacheMaxSize)
                {
                    try
                    {
                        //_diskRWL.AcquireWriterLock(10000);
                        File.Delete(str);
                        //_diskRWL.ReleaseWriterLock();  
                    }
                    catch (IOException)
                    {

                    }
                    finally
                    {
                        totalFilesSize -= fileLen;
                    }
                    continue;
                }

                if (DiskCacheRemoveAfter != TimeSpan.Zero && fi.CreationTime + DiskCacheRemoveAfter < DateTime.Now)
                {
                    try
                    {
                        // _diskRWL.AcquireWriterLock(10000);
                        File.Delete(str);
                        // _diskRWL.ReleaseWriterLock();
                    }
                    catch (IOException)
                    {

                    }
                    finally
                    {
                        totalFilesSize -= fileLen;
                    }
                    continue;
                }

                if (fi.CreationTime + DiskCacheRemoveAfter >= DateTime.Now || DiskCacheMaxSize == 0 || DiskCacheMaxSize < totalFilesSize)
                {
                    break;
                }
            }
        }

        private static ReaderWriterLockSlim _diskRWL = new ReaderWriterLockSlim();

        public static Image getImageFromCache(string cardName)
        {
            if (cardName == null) return null;
            if (AllowDiskCaching == false) return null;

            CheckCache();

            if (string.IsNullOrEmpty(DiskCachePath) || !Directory.Exists(DiskCachePath)) return null;

            string filename = DiskCachePath + getDefaultCardPictureFilename(cardName);
            if (File.Exists(filename))
            {
                _diskRWL.EnterReadLock();
                Image img = Image.FromFile(filename);
                _diskRWL.ExitReadLock();
                return img;
            }
            return null;
        }

        public static string getDefaultCardPictureFilename(string cardName)
        {

            return cardName.Replace(' ', '_').Replace(':', '_') + ".jpg";
        }

        private static void saveImageToCache(string cardName, Image cardImage)
        {
            if (!Directory.Exists(DiskCachePath)) Directory.CreateDirectory(DiskCachePath);
            string filename = DiskCachePath + getDefaultCardPictureFilename(cardName);
            if (File.Exists(filename))
            {
                try
                {
                    File.Delete(filename);
                }
                catch (Exception) { }
            }

            _diskRWL.EnterWriteLock();
            try
            {
                cardImage.Save(filename);
            }
            catch (Exception) { };
            _diskRWL.ExitWriteLock();
        }

        public static Image downloadCardPicture(string cardName)
        {
            switch (DownloadPicturesFrom)
            {
                case EDownloadPicturesFrom.MtgVault_com:
                    return downloadCardPictureMtgVault(cardName);
                case EDownloadPicturesFrom.MagicCards_info:
                default:
                    return downloadCardPictureMagicCards(cardName);
            }
        }

        #region MtgVault.com Picture Download
        private static string constructAddressMtgVault(string cardName)
        {
            if (cardName == null) return null;
            cardName = cardName.Trim();
            cardName.Replace(' ', '+');
            string address = "http://mtgvault.com/ViewCard.aspx?CardName=" + cardName;
            return address;
        }

        private static string getImageAddressMtgVault(string target)
        {
            if (target[0] == '/')
                return "http://mtgvault.com" + target;
            else return "http://mtgvault.com/" + target;
        }

        private static Image downloadCardPictureMtgVault(string cardName)
        {
            if (cardName == null) return null;
            Image outImage = null;
            try
            {
                GeneralTools.HtmlTools.parsePage(new Uri(constructAddressMtgVault(cardName)), "//img[@id='ctl00_ContentPlaceHolder1_Image_Card']", delegate(HtmlNode targetElement, int row, int totalRows)
                {
                    string link = targetElement.GetAttributeValue("src", "");
                    if (link.Length < 1) return;
                    string imagename = link.Split(new char[] { '/' }).Last();

                    if (imagename.Equals("nopicture.jpg"))
                    {
                        outImage = null;
                        return;
                    }
                    else
                    {
                        HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(getImageAddressMtgVault(link));
                        myReq.Timeout = 30000;

                        using (HttpWebResponse response = (HttpWebResponse)myReq.GetResponse())
                        {
                            using (Stream s = response.GetResponseStream())
                            {
                                Image img = Image.FromStream(s);
                                outImage = img;
                            }
                        }
                        myReq.Abort();
                    }
                });
            }
            catch (Exception)
            {
                throw;
            }

            return outImage;
        }
        #endregion
        #region Magiccards.info Picture Download

        private static string constructAddressMagicCards(string cardName)
        {
            if (cardName == null) return null;
            cardName = cardName.Trim();
            cardName.Replace(' ', '+');
            string address = "http://magiccards.info/query?q=!" + cardName;
            return address;
        }

        private static string getImageAddressMagicCards(string target)
        {
            if (target.StartsWith("http://")) return target;

            if (target[0] == '/')
                return "http://magiccards.info" + target;
            else return "http://magiccards.info/" + target;
        }

        private static Image downloadCardPictureMagicCards(string cardName)
        {
            if (cardName == null) return null;
            Image outImage = null;
            try
            {
                GeneralTools.HtmlTools.parsePage(new Uri(constructAddressMagicCards(cardName)), "//table//img", delegate(HtmlNode targetElement, int row, int totalRows)
                {
                    string link = targetElement.GetAttributeValue("src", "");
                    string height = targetElement.GetAttributeValue("height", "");
                    if (height == null || height.Length == 0) return;

                    int iheight = int.Parse(height);
                    if (iheight < 200) return;

                    if (link.Length < 1) return;
                    string imagename = link.Split(new char[] { '/' }).Last();

                    HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(getImageAddressMagicCards(link));
                    myReq.Timeout = 30000;

                    using (HttpWebResponse response = (HttpWebResponse)myReq.GetResponse())
                    {
                        using (Stream _s = response.GetResponseStream())
                        {
                            Image img = Image.FromStream(_s);
                            outImage = img;
                        }
                    }
                    myReq.Abort();
                });
            }
          /*  catch (ArgumentException)
            {
                outImage = null;
            }*/
            catch (Exception)
            {
                throw;
            }

            return outImage;
        }
        #endregion
    }
}
